aviutl2\generic\binding/
edit_section.rs

1use std::num::NonZero;
2
3use crate::common::Rational32;
4
5/// オブジェクトへのハンドル。
6#[derive(Debug, Clone, Copy)]
7pub struct ObjectHandle {
8    pub(crate) internal: aviutl2_sys::plugin2::OBJECT_HANDLE,
9}
10impl From<aviutl2_sys::plugin2::OBJECT_HANDLE> for ObjectHandle {
11    fn from(value: aviutl2_sys::plugin2::OBJECT_HANDLE) -> Self {
12        Self { internal: value }
13    }
14}
15impl From<ObjectHandle> for aviutl2_sys::plugin2::OBJECT_HANDLE {
16    fn from(value: ObjectHandle) -> Self {
17        value.internal
18    }
19}
20
21// 動いたし、このObjectHandleをグローバルに持っておくことも想定されてそうなので多分大丈夫なはず
22unsafe impl Send for ObjectHandle {}
23unsafe impl Sync for ObjectHandle {}
24
25/// 編集情報構造体。
26///
27/// # Note
28///
29/// UI表示と異なり、フレーム番号・レイヤー番号は0始まりです。
30#[derive(Debug, Clone, Copy)]
31pub struct EditInfo {
32    /// シーンの幅。
33    pub width: usize,
34    /// シーンの高さ。
35    pub height: usize,
36    /// フレームレート。
37    pub fps: Rational32,
38    /// サンプルレート。
39    pub sample_rate: usize,
40    /// 現在のカーソルのフレーム番号。
41    pub frame: usize,
42    /// 現在の選択レイヤー番号。
43    pub layer: usize,
44    /// オブジェクトが存在する最大のフレーム番号。
45    pub frame_max: usize,
46    /// オブジェクトが存在する最大のレイヤー番号。
47    pub layer_max: usize,
48    /// レイヤー編集で表示されているフレームの開始番号。
49    pub display_frame_start: usize,
50    /// レイヤー編集で表示されているレイヤーの開始番号。
51    pub display_layer_start: usize,
52    /// レイヤー編集で表示されているフレーム数。
53    ///
54    /// # Note
55    ///
56    /// この値は厳密な値ではありません。
57    pub display_frame_num: usize,
58    /// レイヤー編集で表示されているレイヤー数
59    ///
60    /// # Note
61    ///
62    /// この値は厳密な値ではありません。
63    pub display_layer_num: usize,
64    /// フレーム範囲選択の開始フレーム番号
65    /// 未選択の場合は`None`になります。
66    pub select_range_start: Option<usize>,
67    /// フレーム範囲選択の終了フレーム番号。
68    /// 未選択の場合は`None`になります。
69    pub select_range_end: Option<usize>,
70    /// グリッド(BPM)のテンポ。
71    pub grid_bpm_tempo: f32,
72    /// グリッド(BPM)の拍数。
73    pub grid_bpm_beat: usize,
74    /// グリッド(BPM)の基準時間。
75    pub grid_bpm_offset: f32,
76    /// シーンのID
77    pub scene_id: i32,
78}
79
80impl EditInfo {
81    /// # Safety
82    ///
83    /// `ptr`は有効な`EDIT_INFO`ポインタである必要があります。
84    pub unsafe fn from_raw(ptr: *const aviutl2_sys::plugin2::EDIT_INFO) -> Self {
85        let raw = unsafe { &*ptr };
86        Self {
87            width: raw.width as usize,
88            height: raw.height as usize,
89            fps: Rational32::new(raw.rate, raw.scale),
90            sample_rate: raw.sample_rate as usize,
91            frame: raw.frame as usize,
92            layer: raw.layer as usize,
93            frame_max: raw.frame_max as usize,
94            layer_max: raw.layer_max as usize,
95            display_frame_start: raw.display_frame_start as usize,
96            display_layer_start: raw.display_layer_start as usize,
97            display_frame_num: raw.display_frame_num as usize,
98            display_layer_num: raw.display_layer_num as usize,
99
100            // `as usize` はpanicしないけど一応気分的に安全にしておく、それはそうと
101            // clippyはこれをunnecessary_lazy_evaluationsと判断するので無視する
102            #[expect(clippy::unnecessary_lazy_evaluations)]
103            select_range_start: (raw.select_range_start >= 0)
104                .then(|| raw.select_range_start as usize),
105            #[expect(clippy::unnecessary_lazy_evaluations)]
106            select_range_end: (raw.select_range_end >= 0).then(|| raw.select_range_end as usize),
107
108            grid_bpm_tempo: raw.grid_bpm_tempo,
109            grid_bpm_beat: raw.grid_bpm_beat as usize,
110            grid_bpm_offset: raw.grid_bpm_offset,
111            scene_id: raw.scene_id,
112        }
113    }
114}
115
116/// オブジェクトのレイヤーとフレーム情報。
117#[derive(Debug, Clone, Copy)]
118pub struct ObjectLayerFrame {
119    pub layer: usize,
120    pub start: usize,
121    pub end: usize,
122}
123
124/// レイヤーとフレーム情報。
125#[derive(Debug, Clone, Copy)]
126pub struct LayerFrameData {
127    pub layer: usize,
128    pub frame: usize,
129}
130
131#[derive(Debug, Clone, Copy)]
132pub struct MediaInfo {
133    /// Videoトラック数。
134    pub video_track_num: Option<NonZero<usize>>,
135    /// Audioトラック数。
136    pub audio_track_num: Option<NonZero<usize>>,
137    /// 総時間(秒)。
138    pub total_time: f64,
139    /// 解像度の幅。
140    pub width: usize,
141    /// 解像度の高さ。
142    pub height: usize,
143}
144
145/// [`EditSection::is_support_media_file`] のモード。
146#[derive(Debug, Clone, Copy)]
147pub enum MediaFileSupportMode {
148    /// 拡張子が対応しているかどうかのみを確認します。
149    ExtensionOnly,
150    /// 実際にファイルを開いて対応しているかどうかを確認します。
151    Strict,
152}
153
154/// [`EditSection`] 関連のエラー。
155#[derive(thiserror::Error, Debug)]
156pub enum EditSectionError {
157    #[error("api call failed")]
158    ApiCallFailed,
159    #[error("object does not exist")]
160    ObjectDoesNotExist,
161    #[error("input utf-8 string contains null byte")]
162    InputCstrContainsNull(#[from] std::ffi::NulError),
163    #[error("input utf-16 string contains null byte")]
164    InputCwstrContainsNull(#[from] crate::common::NullByteError),
165    #[error("value is out of range")]
166    ValueOutOfRange(#[from] std::num::TryFromIntError),
167    #[error("api returned non-utf8 data")]
168    NonUtf8Data(#[from] std::str::Utf8Error),
169
170    #[cfg(feature = "aviutl2-alias")]
171    #[error("alias parse error: {0}")]
172    ParseFailed(#[from] aviutl2_alias::TableParseError),
173}
174
175pub type EditSectionResult<T> = Result<T, EditSectionError>;
176
177/// 編集セクションのハンドル。
178#[derive(Debug)]
179pub struct EditSection {
180    /// 編集情報。
181    pub info: EditInfo,
182
183    pub(crate) internal: *mut aviutl2_sys::plugin2::EDIT_SECTION,
184}
185
186impl EditSection {
187    /// 生ポインタから `EditSection` を作成します。
188    ///
189    /// # Safety
190    ///
191    /// 有効な `EDIT_SECTION` ポインタである必要があります。
192    pub unsafe fn from_raw(ptr: *mut aviutl2_sys::plugin2::EDIT_SECTION) -> Self {
193        Self {
194            internal: ptr,
195            info: unsafe { EditInfo::from_raw((*ptr).info) },
196        }
197    }
198
199    /// オブジェクトエイリアスから指定の位置にオブジェクトを作成します。
200    ///
201    /// # Arguments
202    ///
203    /// - `alias`:オブジェクトエイリアスのデータ。オブジェクトエイリアスと同じフォーマットで指定します。
204    /// - `layer`:作成するオブジェクトのレイヤー番号(0始まり)。
205    /// - `frame`:作成するオブジェクトのフレーム番号(0始まり)。
206    /// - `length`:作成するオブジェクトの長さ(フレーム数)。
207    ///
208    /// lengthはエイリアスデータにフレーム情報が無い場合に利用されます。
209    ///
210    /// # Errors
211    ///
212    /// エイリアスの変換に失敗した場合、またはオブジェクトが既存のオブジェクトに重なる場合にエラー
213    ///
214    pub fn create_object_from_alias(
215        &self,
216        alias: &str,
217        layer: usize,
218        frame: usize,
219        length: usize,
220    ) -> EditSectionResult<ObjectHandle> {
221        let c_alias = std::ffi::CString::new(alias)?;
222        let object_handle = unsafe {
223            ((*self.internal).create_object_from_alias)(
224                c_alias.as_ptr(),
225                layer.try_into()?,
226                frame.try_into()?,
227                length.try_into()?,
228            )
229        };
230        if object_handle.is_null() {
231            return Err(EditSectionError::ApiCallFailed);
232        }
233        Ok(ObjectHandle {
234            internal: object_handle,
235        })
236    }
237
238    /// 指定のフレーム番号以降にあるオブジェクトを検索します。
239    ///
240    /// # Arguments
241    ///
242    /// - `layer`:検索するレイヤー番号(0始まり)。
243    /// - `frame`:検索を開始するフレーム番号(0始まり)。
244    pub fn find_object_after(
245        &self,
246        layer: usize,
247        frame: usize,
248    ) -> EditSectionResult<Option<ObjectHandle>> {
249        let object_handle =
250            unsafe { ((*self.internal).find_object)(layer.try_into()?, frame.try_into()?) };
251        if object_handle.is_null() {
252            Ok(None)
253        } else {
254            Ok(Some(ObjectHandle {
255                internal: object_handle,
256            }))
257        }
258    }
259
260    /// オブジェクトに対象エフェクトが何個存在するかを取得します。
261    ///
262    /// # Arguments
263    ///
264    /// - `object`:対象のオブジェクトハンドル。
265    /// - `effect`:対象のエフェクト名。(エイリアスファイルの effect.name の値)
266    ///
267    /// # Returns
268    ///
269    /// 対象エフェクトの数。存在しない場合は0を返します。
270    pub fn count_object_effect(
271        &self,
272        object: &ObjectHandle,
273        effect: &str,
274    ) -> EditSectionResult<usize> {
275        self.ensure_object_exists(object)?;
276        let c_effect = crate::common::CWString::new(effect)?;
277        let count =
278            unsafe { ((*self.internal).count_object_effect)(object.internal, c_effect.as_ptr()) };
279        Ok(count.try_into()?)
280    }
281
282    /// 指定のオブジェクトのレイヤーとフレーム情報を取得します。
283    pub fn get_object_layer_frame(
284        &self,
285        object: &ObjectHandle,
286    ) -> EditSectionResult<ObjectLayerFrame> {
287        self.ensure_object_exists(object)?;
288        let object = unsafe { ((*self.internal).get_object_layer_frame)(object.internal) };
289        Ok(ObjectLayerFrame {
290            layer: object.layer.try_into()?,
291            start: object.start.try_into()?,
292            end: object.end.try_into()?,
293        })
294    }
295
296    /// オブジェクトの情報をエイリアスデータとして取得します。
297    pub fn get_object_alias(&self, object: &ObjectHandle) -> EditSectionResult<String> {
298        self.ensure_object_exists(object)?;
299        let alias_ptr = unsafe { ((*self.internal).get_object_alias)(object.internal) };
300        if alias_ptr.is_null() {
301            return Err(EditSectionError::ApiCallFailed);
302        }
303        let c_str = unsafe { std::ffi::CStr::from_ptr(alias_ptr) };
304        let alias = c_str.to_str()?.to_owned();
305        Ok(alias)
306    }
307
308    /// オブジェクト名を取得します。
309    ///
310    /// # Returns
311    ///
312    /// 標準の名前の場合は`None`を返します。
313    pub fn get_object_name(&self, object: &ObjectHandle) -> EditSectionResult<Option<String>> {
314        self.ensure_object_exists(object)?;
315        let name_ptr = unsafe { ((*self.internal).get_object_name)(object.internal) };
316        if name_ptr.is_null() {
317            return Ok(None);
318        }
319        Ok(Some(unsafe { crate::common::load_wide_string(name_ptr) }))
320    }
321
322    /// オブジェクト名を設定します。
323    ///
324    /// # Note
325    ///
326    /// `name`に`None`や空文字を指定すると、標準の名前になります。
327    pub fn set_object_name(
328        &self,
329        object: &ObjectHandle,
330        name: Option<&str>,
331    ) -> EditSectionResult<()> {
332        self.ensure_object_exists(object)?;
333        match name {
334            None => {
335                unsafe { ((*self.internal).set_object_name)(object.internal, std::ptr::null()) };
336                Ok(())
337            }
338            Some(name) => {
339                let c_name = crate::common::CWString::new(name)?;
340                unsafe {
341                    ((*self.internal).set_object_name)(object.internal, c_name.as_ptr());
342                }
343                Ok(())
344            }
345        }
346    }
347
348    /// オブジェクトの設定項目の値を文字列で取得します。
349    ///
350    /// # Arguments
351    ///
352    /// - `object`:対象のオブジェクトハンドル。
353    /// - `effect_name`:設定項目の名前。
354    /// - `effect_index`:同じ名前の設定項目が複数ある場合のインデックス(0始まり)。
355    /// - `item`:設定項目の名前。(エイリアスファイルのキーの名前)
356    pub fn get_object_effect_item(
357        &self,
358        object: &ObjectHandle,
359        effect_name: &str,
360        effect_index: usize,
361        item: &str,
362    ) -> EditSectionResult<String> {
363        self.ensure_object_exists(object)?;
364        let c_effect_name = crate::common::CWString::new(&effect_key(effect_name, effect_index))?;
365        let c_item = crate::common::CWString::new(item)?;
366        let value_ptr = unsafe {
367            ((*self.internal).get_object_item_value)(
368                object.internal,
369                c_effect_name.as_ptr(),
370                c_item.as_ptr(),
371            )
372        };
373        if value_ptr.is_null() {
374            return Err(EditSectionError::ApiCallFailed);
375        }
376        let c_str = unsafe { std::ffi::CStr::from_ptr(value_ptr) };
377        let value = c_str.to_str()?.to_owned();
378        Ok(value)
379    }
380
381    /// オブジェクトの設定項目の値を文字列で設定します。
382    ///
383    /// # Arguments
384    ///
385    /// - `object`:対象のオブジェクトハンドル。
386    /// - `effect_name`:設定項目の名前。
387    /// - `effect_index`:同じ名前の設定項目が複数ある場合のインデックス(0始まり)。
388    /// - `item`:設定項目の名前。(エイリアスファイルのキーの名前)
389    /// - `value`:設定する値。
390    pub fn set_object_effect_item(
391        &self,
392        object: &ObjectHandle,
393        effect_name: &str,
394        effect_index: usize,
395        item: &str,
396        value: &str,
397    ) -> EditSectionResult<()> {
398        self.ensure_object_exists(object)?;
399        let c_effect_name = crate::common::CWString::new(&effect_key(effect_name, effect_index))?;
400        let c_item = crate::common::CWString::new(item)?;
401        let c_value = std::ffi::CString::new(value)?;
402        let success = unsafe {
403            ((*self.internal).set_object_item_value)(
404                object.internal,
405                c_effect_name.as_ptr(),
406                c_item.as_ptr(),
407                c_value.as_ptr(),
408            )
409        };
410        if !success {
411            return Err(EditSectionError::ApiCallFailed);
412        }
413        Ok(())
414    }
415
416    /// オブジェクトを移動します。
417    pub fn move_object(
418        &self,
419        object: &ObjectHandle,
420        new_layer: usize,
421        new_start_frame: usize,
422    ) -> EditSectionResult<()> {
423        self.ensure_object_exists(object)?;
424        let success = unsafe {
425            ((*self.internal).move_object)(
426                object.internal,
427                new_layer.try_into()?,
428                new_start_frame.try_into()?,
429            )
430        };
431        if !success {
432            return Err(EditSectionError::ApiCallFailed);
433        }
434        Ok(())
435    }
436
437    /// オブジェクトを削除します。
438    pub fn delete_object(&self, object: &ObjectHandle) -> EditSectionResult<()> {
439        self.ensure_object_exists(object)?;
440        unsafe { ((*self.internal).delete_object)(object.internal) };
441        Ok(())
442    }
443
444    /// 現在、オブジェクト設定ウィンドウで選択されているオブジェクトを取得します。
445    pub fn get_focused_object(&self) -> EditSectionResult<Option<ObjectHandle>> {
446        let object_handle = unsafe { ((*self.internal).get_focus_object)() };
447        if object_handle.is_null() {
448            Ok(None)
449        } else {
450            Ok(Some(ObjectHandle {
451                internal: object_handle,
452            }))
453        }
454    }
455
456    /// 現在選択されているオブジェクトの一覧を取得します。
457    pub fn get_selected_objects(&self) -> EditSectionResult<Vec<ObjectHandle>> {
458        let mut handles = Vec::new();
459        let num_objects = unsafe { ((*self.internal).get_selected_object_num)() };
460        for i in 0..num_objects {
461            let object_handle = unsafe { ((*self.internal).get_selected_object)(i) };
462            if object_handle.is_null() {
463                return Err(EditSectionError::ApiCallFailed);
464            }
465            handles.push(ObjectHandle {
466                internal: object_handle,
467            });
468        }
469        Ok(handles)
470    }
471
472    /// オブジェクト設定ウィンドウで指定のオブジェクトを選択状態にします。
473    ///
474    /// # Note
475    ///
476    /// コールバック処理の終了時に設定されます。
477    pub fn focus_object(&self, object: &ObjectHandle) -> EditSectionResult<()> {
478        self.ensure_object_exists(object)?;
479        unsafe { ((*self.internal).set_focus_object)(object.internal) };
480        Ok(())
481    }
482
483    /// プロジェクトファイルのポインタを取得します。
484    pub fn get_project_file<'handle>(
485        &'handle self,
486        edit_handle: &crate::generic::EditHandle,
487    ) -> crate::generic::ProjectFile<'handle> {
488        let pf_ptr = unsafe { ((*self.internal).get_project_file)(edit_handle.internal) };
489        unsafe { crate::generic::ProjectFile::from_raw(pf_ptr) }
490    }
491
492    /// マウス座標のレイヤー・フレーム位置を取得します。
493    ///
494    /// # Returns
495    ///
496    /// マウスがレイヤー編集エリア内にある場合は `Some` を返し、
497    /// そうでない場合は `None` を返します。
498    pub fn get_mouse_layer_frame(&self) -> EditSectionResult<Option<LayerFrameData>> {
499        let mut layer = 0;
500        let mut frame = 0;
501        let on_layer_edit =
502            unsafe { ((*self.internal).get_mouse_layer_frame)(&mut layer, &mut frame) };
503        if on_layer_edit {
504            Ok(Some(LayerFrameData {
505                layer: layer.try_into()?,
506                frame: frame.try_into()?,
507            }))
508        } else {
509            Ok(None)
510        }
511    }
512
513    /// 指定のスクリーン座標のレイヤー・フレーム位置を取得します。
514    pub fn pos_to_layer_frame(&self, x: i32, y: i32) -> EditSectionResult<Option<LayerFrameData>> {
515        let mut layer = 0;
516        let mut frame = 0;
517        let on_layer_edit =
518            unsafe { ((*self.internal).pos_to_layer_frame)(x, y, &mut layer, &mut frame) };
519        if on_layer_edit {
520            Ok(Some(LayerFrameData {
521                layer: layer.try_into()?,
522                frame: frame.try_into()?,
523            }))
524        } else {
525            Ok(None)
526        }
527    }
528
529    /// 指定のメディアファイルがサポートされているかどうか調べます。
530    pub fn is_support_media_file<P: AsRef<str>>(
531        &self,
532        file_path: P,
533        mode: MediaFileSupportMode,
534    ) -> EditSectionResult<bool> {
535        let c_file_path = crate::common::CWString::new(file_path.as_ref())?;
536        let is_supported = unsafe {
537            match mode {
538                MediaFileSupportMode::ExtensionOnly => {
539                    ((*self.internal).is_support_media_file)(c_file_path.as_ptr(), false)
540                }
541                MediaFileSupportMode::Strict => {
542                    ((*self.internal).is_support_media_file)(c_file_path.as_ptr(), true)
543                }
544            }
545        };
546        Ok(is_supported)
547    }
548
549    /// 指定のメディアファイルの情報を取得します。
550    ///
551    /// # Note
552    ///
553    /// 動画、音声、画像ファイル以外では取得出来ません。
554    pub fn get_media_info<P: AsRef<str>>(&self, file_path: P) -> EditSectionResult<MediaInfo> {
555        let c_file_path = crate::common::CWString::new(file_path.as_ref())?;
556        let mut media_info = std::mem::MaybeUninit::<aviutl2_sys::plugin2::MEDIA_INFO>::uninit();
557        let success = unsafe {
558            ((*self.internal).get_media_info)(
559                c_file_path.as_ptr(),
560                media_info.as_mut_ptr(),
561                std::mem::size_of::<aviutl2_sys::plugin2::MEDIA_INFO>() as i32,
562            )
563        };
564        if !success {
565            return Err(EditSectionError::ApiCallFailed);
566        }
567        let media_info = unsafe { media_info.assume_init() };
568        Ok(MediaInfo {
569            video_track_num: NonZero::new(media_info.video_track_num.try_into()?),
570            audio_track_num: NonZero::new(media_info.audio_track_num.try_into()?),
571            total_time: media_info.total_time,
572            width: media_info.width.try_into()?,
573            height: media_info.height.try_into()?,
574        })
575    }
576
577    /// 指定の位置にメディアファイルからオブジェクトを作成します。
578    ///
579    /// # Arguments
580    ///
581    /// - `file_path`:メディアファイルのパス。
582    /// - `layer`:作成するオブジェクトのレイヤー番号(0始まり)。
583    /// - `frame`:作成するオブジェクトのフレーム番号(0始まり)。
584    /// - `length`:作成するオブジェクトの長さ(フレーム数)。`None`を指定した場合、長さや追加位置は自動的に調整されます。
585    pub fn create_object_from_media_file<P: AsRef<str>>(
586        &self,
587        file_path: P,
588        layer: usize,
589        frame: usize,
590        length: Option<usize>,
591    ) -> EditSectionResult<ObjectHandle> {
592        let c_file_path = crate::common::CWString::new(file_path.as_ref())?;
593        let object_handle = unsafe {
594            ((*self.internal).create_object_from_media_file)(
595                c_file_path.as_ptr(),
596                layer.try_into()?,
597                frame.try_into()?,
598                length.unwrap_or(0).try_into()?,
599            )
600        };
601        if object_handle.is_null() {
602            return Err(EditSectionError::ApiCallFailed);
603        }
604        Ok(ObjectHandle {
605            internal: object_handle,
606        })
607    }
608
609    /// 指定の位置にオブジェクトを作成します。
610    ///
611    /// # Arguments
612    ///
613    /// - `effect`:エフェクト名。(エイリアスファイルの effect.name の値)
614    /// - `layer`:作成するオブジェクトのレイヤー番号(0始まり)。
615    /// - `frame`:作成するオブジェクトのフレーム番号(0始まり)。
616    /// - `length`:作成するオブジェクトの長さ(フレーム数)。`None`を指定した場合、長さや追加位置は自動的に調整されます。
617    pub fn create_object(
618        &self,
619        effect: &str,
620        layer: usize,
621        frame: usize,
622        length: Option<usize>,
623    ) -> EditSectionResult<ObjectHandle> {
624        let c_effect = crate::common::CWString::new(effect)?;
625        let object_handle = unsafe {
626            ((*self.internal).create_object)(
627                c_effect.as_ptr(),
628                layer.try_into()?,
629                frame.try_into()?,
630                length.unwrap_or(0).try_into()?,
631            )
632        };
633        if object_handle.is_null() {
634            return Err(EditSectionError::ApiCallFailed);
635        }
636        Ok(ObjectHandle {
637            internal: object_handle,
638        })
639    }
640
641    /// 現在のレイヤー・フレーム位置を設定します。
642    ///
643    /// # Note
644    ///
645    /// 設定出来る範囲に調整されます。
646    pub fn set_cursor_layer_frame(&self, layer: usize, frame: usize) -> EditSectionResult<()> {
647        unsafe {
648            ((*self.internal).set_cursor_layer_frame)(layer.try_into()?, frame.try_into()?);
649        }
650        Ok(())
651    }
652
653    /// レイヤー編集のレイヤー・フレームの表示開始位置を設定します。
654    ///
655    /// # Note
656    ///
657    /// 設定出来る範囲に調整されます。
658    pub fn set_display_layer_frame(&self, layer: usize, frame: usize) -> EditSectionResult<()> {
659        unsafe {
660            ((*self.internal).set_display_layer_frame)(layer.try_into()?, frame.try_into()?);
661        }
662        Ok(())
663    }
664
665    /// フレーム範囲選択を設定します。
666    ///
667    /// # Note
668    ///
669    /// 設定出来る範囲に調整されます。
670    pub fn set_select_range(&self, start: usize, end: usize) -> EditSectionResult<()> {
671        unsafe {
672            ((*self.internal).set_select_range)(start.try_into()?, end.try_into()?);
673        }
674        Ok(())
675    }
676
677    /// 選択されているフレーム範囲選択を解除します。
678    pub fn clear_select_range(&self) -> EditSectionResult<()> {
679        unsafe {
680            ((*self.internal).set_select_range)(-1, -1);
681        }
682        Ok(())
683    }
684
685    /// グリッド(BPM)を設定します。
686    pub fn set_grid_bpm(&self, tempo: f32, beat: usize, offset: f32) -> EditSectionResult<()> {
687        unsafe {
688            ((*self.internal).set_grid_bpm)(tempo, beat.try_into()?, offset);
689        }
690        Ok(())
691    }
692
693    /// オブジェクトが存在するかどうか調べます。
694    ///
695    /// # Note
696    ///
697    /// 内部的には、get_object_layer_frame を呼び出してレイヤー番号が -1 でないかを確認しています。
698    pub fn object_exists(&self, object: &ObjectHandle) -> bool {
699        let object = unsafe { ((*self.internal).get_object_layer_frame)(object.internal) };
700        object.layer != -1
701    }
702
703    fn ensure_object_exists(&self, object: &ObjectHandle) -> EditSectionResult<()> {
704        if !self.object_exists(object) {
705            return Err(EditSectionError::ObjectDoesNotExist);
706        }
707        Ok(())
708    }
709
710    /// すべてのレイヤーをイテレータで取得します。
711    pub fn layers(&self) -> EditSectionLayersIterator<'_> {
712        EditSectionLayersIterator::new(self)
713    }
714
715    /// [EditSectionLayerCaller] を作成します。
716    pub fn layer<'a>(&'a self, layer: usize) -> EditSectionLayerCaller<'a> {
717        EditSectionLayerCaller::new(self, layer)
718    }
719    /// [EditSectionObjectCaller] を作成します。
720    pub fn object<'a>(&'a self, object: &'a ObjectHandle) -> EditSectionObjectCaller<'a> {
721        EditSectionObjectCaller::new(self, object)
722    }
723}
724
725/// オブジェクト主体で関数を呼び出すための構造体。
726/// EditSection と ObjectHandle の組をまとめ、対象オブジェクトに対する
727/// 操作を簡潔に呼び出せるようにします。
728pub struct EditSectionObjectCaller<'a> {
729    edit_section: &'a EditSection,
730    pub handle: &'a ObjectHandle,
731}
732impl<'a> EditSectionObjectCaller<'a> {
733    pub fn new(edit_section: &'a EditSection, object: &'a ObjectHandle) -> Self {
734        Self {
735            edit_section,
736            handle: object,
737        }
738    }
739
740    /// オブジェクトのレイヤーとフレーム情報を取得します。
741    pub fn get_layer_frame(&self) -> EditSectionResult<ObjectLayerFrame> {
742        self.edit_section.get_object_layer_frame(self.handle)
743    }
744
745    /// オブジェクトの情報をエイリアスデータとして取得します。
746    pub fn get_alias(&self) -> EditSectionResult<String> {
747        self.edit_section.get_object_alias(self.handle)
748    }
749
750    /// オブジェクトの情報をエイリアスデータとして取得し、パースします。
751    #[cfg(feature = "aviutl2-alias")]
752    pub fn get_alias_parsed(&self) -> EditSectionResult<aviutl2_alias::Table> {
753        self.edit_section
754            .get_object_alias(self.handle)?
755            .parse()
756            .map_err(Into::into)
757    }
758
759    /// オブジェクトに対象エフェクトが何個存在するかを取得します。
760    ///
761    /// # Arguments
762    ///
763    /// - `effect`:対象のエフェクト名。(エイリアスファイルの effect.name の値)
764    ///
765    /// # Returns
766    ///
767    /// 対象エフェクトの数。存在しない場合は0を返します。
768    pub fn count_effect(&self, effect: &str) -> EditSectionResult<usize> {
769        self.edit_section.count_object_effect(self.handle, effect)
770    }
771
772    /// オブジェクトの設定項目の値を文字列で取得します。
773    ///
774    /// # Arguments
775    ///
776    /// - `effect_name`:設定項目の名前。
777    /// - `effect_index`:同じ名前の設定項目が複数ある場合のインデックス(0始まり)。
778    /// - `item`:設定項目の名前。(エイリアスファイルのキーの名前)
779    pub fn get_effect_item(
780        &self,
781        effect_name: &str,
782        effect_index: usize,
783        item: &str,
784    ) -> EditSectionResult<String> {
785        self.edit_section
786            .get_object_effect_item(self.handle, effect_name, effect_index, item)
787    }
788
789    /// オブジェクトの設定項目の値を文字列で設定します。
790    ///
791    /// # Arguments
792    ///
793    /// - `effect_name`:設定項目の名前。
794    /// - `effect_index`:同じ名前の設定項目が複数ある場合のインデックス(0始まり)。
795    /// - `item`:設定項目の名前。(エイリアスファイルのキーの名前)
796    /// - `value`:設定する値。
797    pub fn set_effect_item(
798        &self,
799        effect_name: &str,
800        effect_index: usize,
801        item: &str,
802        value: &str,
803    ) -> EditSectionResult<()> {
804        self.edit_section.set_object_effect_item(
805            self.handle,
806            effect_name,
807            effect_index,
808            item,
809            value,
810        )
811    }
812
813    /// オブジェクトを移動します。
814    ///
815    /// # Arguments
816    ///
817    /// - `new_layer`:移動先のレイヤー番号(0始まり)。
818    /// - `new_start_frame`:移動先の開始フレーム番号(0始まり)。
819    pub fn move_object(&self, new_layer: usize, new_start_frame: usize) -> EditSectionResult<()> {
820        self.edit_section
821            .move_object(self.handle, new_layer, new_start_frame)
822    }
823
824    /// オブジェクトを削除します。
825    pub fn delete_object(&self) -> EditSectionResult<()> {
826        self.edit_section.delete_object(self.handle)
827    }
828
829    /// オブジェクト設定ウィンドウでこのオブジェクトを選択状態にします。
830    ///
831    /// # Note
832    ///
833    /// コールバック処理の終了時に設定されます。
834    pub fn focus_object(&self) -> EditSectionResult<()> {
835        self.edit_section.focus_object(self.handle)
836    }
837
838    /// このオブジェクトが存在するかどうか調べます。
839    pub fn exists(&self) -> bool {
840        self.edit_section.object_exists(self.handle)
841    }
842}
843
844/// レイヤー主体で関数を呼び出すための構造体。
845/// EditSection と レイヤー番号 の組をまとめ、対象レイヤーに対する
846/// 操作を簡潔に呼び出せるようにします。
847pub struct EditSectionLayerCaller<'a> {
848    edit_section: &'a EditSection,
849    pub index: usize,
850}
851impl<'a> EditSectionLayerCaller<'a> {
852    pub fn new(edit_section: &'a EditSection, layer: usize) -> Self {
853        Self {
854            edit_section,
855            index: layer,
856        }
857    }
858
859    /// 指定のフレーム番号以降にあるオブジェクトを検索します。
860    ///
861    /// # Arguments
862    ///
863    /// - `frame`:検索を開始するフレーム番号(0始まり)。
864    pub fn find_object_after(&self, frame: usize) -> EditSectionResult<Option<ObjectHandle>> {
865        self.edit_section.find_object_after(self.index, frame)
866    }
867
868    /// 指定の位置にメディアファイルからオブジェクトを作成します。
869    ///
870    /// # See Also
871    ///
872    /// [`EditSection::create_object_from_media_file`]
873    pub fn create_object_from_media_file<P: AsRef<str>>(
874        &self,
875        file_path: P,
876        frame: usize,
877        length: Option<usize>,
878    ) -> EditSectionResult<ObjectHandle> {
879        self.edit_section
880            .create_object_from_media_file(file_path, self.index, frame, length)
881    }
882
883    /// 指定の位置にオブジェクトを作成します。
884    ///
885    /// # See Also
886    ///
887    /// [`EditSection::create_object`]
888    pub fn create_object(
889        &self,
890        effect: &str,
891        frame: usize,
892        length: Option<usize>,
893    ) -> EditSectionResult<ObjectHandle> {
894        self.edit_section
895            .create_object(effect, self.index, frame, length)
896    }
897
898    /// このレイヤーに存在するすべてのオブジェクトを、
899    /// 開始フレームの昇順で走査するイテレータを返します。
900    pub fn objects(&self) -> EditSectionLayerObjectsIterator<'a> {
901        EditSectionLayerObjectsIterator::new(self.edit_section, self.index)
902    }
903}
904
905/// レイヤーのイテレータ。
906#[derive(Debug, Clone)]
907pub struct EditSectionLayersIterator<'a> {
908    edit_section: &'a EditSection,
909    current: usize,
910    total: usize,
911}
912
913impl<'a> EditSectionLayersIterator<'a> {
914    fn new(edit_section: &'a EditSection) -> Self {
915        Self {
916            edit_section,
917            current: 0,
918            total: edit_section.info.layer_max,
919        }
920    }
921}
922
923impl<'a> Iterator for EditSectionLayersIterator<'a> {
924    type Item = EditSectionLayerCaller<'a>;
925
926    fn next(&mut self) -> Option<Self::Item> {
927        if self.current > self.total {
928            return None;
929        }
930        let layer = self.current;
931        self.current += 1;
932        Some(EditSectionLayerCaller::new(self.edit_section, layer))
933    }
934}
935
936/// レイヤー内のオブジェクトを走査するイテレータ。
937/// アイテムは `(オブジェクトのレイヤー・フレーム情報, ハンドル)` の組です。
938#[derive(Debug, Clone)]
939pub struct EditSectionLayerObjectsIterator<'a> {
940    edit_section: &'a EditSection,
941    layer: usize,
942    next_frame: usize,
943}
944
945impl<'a> EditSectionLayerObjectsIterator<'a> {
946    fn new(edit_section: &'a EditSection, layer: usize) -> Self {
947        Self {
948            edit_section,
949            layer,
950            next_frame: 0,
951        }
952    }
953}
954
955impl<'a> Iterator for EditSectionLayerObjectsIterator<'a> {
956    type Item = (ObjectLayerFrame, ObjectHandle);
957
958    fn next(&mut self) -> Option<Self::Item> {
959        // 検索・取得でエラーが出た場合は None を返して終了する。
960        let Ok(Some(handle)) = self
961            .edit_section
962            .find_object_after(self.layer, self.next_frame)
963        else {
964            return None;
965        };
966
967        let lf = match self.edit_section.get_object_layer_frame(&handle) {
968            Ok(lf) => lf,
969            Err(_) => return None,
970        };
971
972        // 次の検索開始位置を、いま見つかったオブジェクトの末尾+1 に進める。
973        self.next_frame = lf.end.saturating_add(1);
974
975        Some((lf, handle))
976    }
977}
978
979fn effect_key(effect_name: &str, effect_index: usize) -> String {
980    format!("{effect_name}:{effect_index}")
981}